﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LittleBirdAuction.Core
{
    public class AuctionConst
    {
        public static readonly int INVALID_ID = 0x7fffffff;
        public static readonly int INVALID_INDEX = INVALID_ID;
        public static readonly int BUFF_LEN = 1024;
        public static readonly int NUMBER_ACCURACY = 1000;

        // private
        //------------------------------------------------------------------------
        public static readonly double FIXED_REZ_X = 2560;
        public static readonly double FIXED_REZ_Y = 1600;

        public static double F2R_Y(double y)
        {
            return ((double)(y) / (double)FIXED_REZ_Y);
        }

        public static double F2R_X(double x)
        {
            return F2R_Y(x);
        }

        public static double F2R_XR(double x)
        {
            return F2R_Y(FIXED_REZ_X - (x));
        }

        public static double F2R_XC(double x)
        {
            return ((Double)((x) - (FIXED_REZ_X / 2)) / (Double)FIXED_REZ_Y);
        }

        public static readonly FilterCharId _C_ALL =
            FilterCharId.FILTER_CHAR_BARBARIAN |
            FilterCharId.FILTER_CHAR_DEMONHUNTER |
            FilterCharId.FILTER_CHAR_DEMONHUNTER |
            FilterCharId.FILTER_CHAR_MONK |
            FilterCharId.FILTER_CHAR_WITCHDOCTOR |
            FilterCharId.FILTER_CHAR_WIZARD;

        public static readonly FilterCharId _C_BARB = FilterCharId.FILTER_CHAR_BARBARIAN;
        public static readonly FilterCharId _C_DH = FilterCharId.FILTER_CHAR_DEMONHUNTER;
        public static readonly FilterCharId _C_MONK = FilterCharId.FILTER_CHAR_MONK;
        public static readonly FilterCharId _C_WD = FilterCharId.FILTER_CHAR_WITCHDOCTOR;
        public static readonly FilterCharId _C_WIZ = FilterCharId.FILTER_CHAR_WIZARD;

        // constants
        //------------------------------------------------------------------------
        public static readonly Double _COORD_HEIGHT = 1200.0;

        public static readonly string _MODULE_D3 = "Diablo III.exe";
        public static readonly string _MODULE_BNET = "battle.net.dll";

        public static readonly uint _TEXT_COMBO_LIMIT = 128;
        public static readonly uint _TEXT_STANDARD_LIMIT = 128;
        public static readonly uint _TEXT_POPUP_LIMIT = 1024;
        public static readonly uint _TEXT_STATS_LIMIT = 1024;
        public static readonly char _CLEAR_CHAR = '_';

        // string formats
        //------------------------------------------------------------------------
        public static readonly string _HINT_DROPDOWN = "Root.TopLayer.DropDown";
        public static readonly string _HINT_DROPDOWNCONTENT = "Root.TopLayer.DropDown._content";
        public static readonly string _HINT_DROPDOWNCONTENTSTACK = "Root.TopLayer.DropDown._content._stackpanel";
        public static readonly string _HINT_LISTITEM_ITEM = "D3_ITEM";

        public static readonly string _FORMAT_COMBO_TEXT = "{c:%s}%s{/c}";
        public static readonly string _FORMAT_ITEM_NAME = "{C:%s}%s{/C}";
        public static readonly string _FORMAT_ITEM_HEADING = "{c:%s}%s{/c}";
        public static readonly string _FORMAT_ITEM_TYPE = "%s %s";
        public static readonly string _FORMAT_ITEM_ILEVEL = "%s: %u";
        public static readonly string _FORMAT_SOCKET_EMPTY = "{c_bonus}%s{/c}";
        public static readonly string _FORMAT_SOCKET_GEM = "{icon:bullet} %s\n";
        public static readonly string[] _FORMAT_STAT = new string[] {
            "{icon:%s} {c:%s}%s{c:ffff0000}%s{/c}{/c}\n",
            "{icon:%s} {c:%s}%s{/c}\n",
            "{c:%s}{icon:%s}%s{c:ffff0000}%s{/c}{/c}\n",
            "{c:%s}{icon:%s}%s{/c}\n",
            "{c:%s}{c:%s}{icon:bullet}%s{/c}{/c}\n",
            "{c:%s}{c:%s}{icon:bullet}%s{c:ffff0000}%s{/c}{/c}\n",
            "{%s:%s}%s{c:ffff0000}%s{/c}{/c}\n" };

        public static readonly string _FORMAT_SHIELD = "+%u%% {c:FF888888}%s{/c:FF888888}\n%u-%u {c:FF888888}%s{/c:FF888888}";
        public static readonly string _FORMAT_WEAPON = "%u-%u {c:FF888888}%s{/c:FF888888}\n%u {c:FF888888}%s{/c:FF888888}";

        // UI object paths
        //------------------------------------------------------------------------
        public static readonly string[] _UIOBJECT_PATH = new string[]
        {
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchRarityFilter",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchCharacterFilter",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchSlotPrimaryFilter",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchSlotSecondaryFilter",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchStatsFilterList.slot 0 list.AdvancedFilterComboBox",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchStatsFilterList.slot 1 list.AdvancedFilterComboBox",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchStatsFilterList.slot 2 list.AdvancedFilterComboBox",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchStatsFilterList.slot 3 list.AdvancedFilterComboBox",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchStatsFilterList.slot 4 list.AdvancedFilterComboBox",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchStatsFilterList.slot 5 list.AdvancedFilterComboBox",

            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.LevelMinInputTextBox",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.LevelMaxInputTextBox",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchStatsFilterList.slot 0 list.AdvancedFilterTextBox",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchStatsFilterList.slot 1 list.AdvancedFilterTextBox",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchStatsFilterList.slot 2 list.AdvancedFilterTextBox",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchStatsFilterList.slot 3 list.AdvancedFilterTextBox",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchStatsFilterList.slot 4 list.AdvancedFilterTextBox",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.SearchStatsFilterList.slot 5 list.AdvancedFilterTextBox",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.UniqueItemTextBox",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.EquipmentSearch.BuyoutTextBox",

            "Root.TopLayer.item 2.stack.frame body.stack.stats",
            "Root.TopLayer.item 2.stack.frame body.stack.main.stack.special_stats",
            "Root.TopLayer.item 2.stack.frame body.stack.main.stack.wrapper.col1.stack.rating",
            "Root.TopLayer.item 2.stack.frame body.stack.main.stack.wrapper.col1.type",
            "Root.TopLayer.item 2.stack.frame body.stack.wrapper.itemLevel",
            "Root.TopLayer.item 2.stack.frame body.stack.socket 0.text",
            "Root.TopLayer.item 2.stack.frame body.stack.socket 1.text",
            "Root.TopLayer.item 2.stack.frame body.stack.socket 2.text",
            "Root.TopLayer.item 2.stack.top_wrapper.stack.name",

            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.TabContentContainer.SearchTabContent.SearchListContent.SearchItemList.PagingButtonsContainer.PageRightButton",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.SearchMenu.SearchMenuContent.SearchItemListContent.SearchButton",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.MenuContentContainer.CompletedMenu.CompletedMenuContainer.CompletedItemContainer.CompletedItemSendToButton",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.TabContentContainer.SearchTabContent.SearchListContent.SearchBidButton",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.TabContentContainer.SearchTabContent.SearchListContent.SearchBuyoutButton",
            "Root.NormalLayer.BattleNetCampaign_main.LayoutRoot.Menu.AuctionHouseButton",

            "Root.TopLayer.BattleNetAuctionHouseNotifications_main.LayoutRoot.ProcessingDialog.LayoutRoot.DialogContent.PendingItemText",
            "Root.TopLayer.BattleNetModalNotifications_main.ModalNotification.Content.List.Message",

            "Root.TopLayer",
            "Root.NormalLayer.BattleNetAuctionHouse_main.LayoutRoot.OverlayContainer.PageHeader",
            "Root.TopLayer.BattleNetModalNotifications_main",
            "Root" };

        public static readonly SettingItem[] LOCALE_ITEMS = new SettingItem[]
        {
            new SettingItem("GEM_STAT_VITALITY",                   "", LocaleGroup.LOCALE_GEMSTAT,   GemStatId.GEM_STAT_VITALITY ),
            new SettingItem( "GEM_STAT_LIFEP",                     "", LocaleGroup.LOCALE_GEMSTAT,   GemStatId.GEM_STAT_LIFEP ),
            new SettingItem( "GEM_STAT_LIFEONHIT",                 "", LocaleGroup.LOCALE_GEMSTAT,   GemStatId.GEM_STAT_LIFEONHIT ),
            new SettingItem( "GEM_STAT_DEXTERITY",                 "", LocaleGroup.LOCALE_GEMSTAT,   GemStatId.GEM_STAT_DEXTERITY ),
            new SettingItem( "GEM_STAT_GOLDFIND",                  "", LocaleGroup.LOCALE_GEMSTAT,   GemStatId.GEM_STAT_GOLDFIND ),
            new SettingItem( "GEM_STAT_CRITICALHITDAMAGE",         "", LocaleGroup.LOCALE_GEMSTAT,   GemStatId.GEM_STAT_CRITICALHITDAMAGE ),
            new SettingItem( "GEM_STAT_STRENGTH",                  "", LocaleGroup.LOCALE_GEMSTAT,   GemStatId.GEM_STAT_STRENGTH ),
            new SettingItem( "GEM_STAT_BONUSEXPERIENCE",           "", LocaleGroup.LOCALE_GEMSTAT,   GemStatId.GEM_STAT_BONUSEXPERIENCE ),
            new SettingItem( "GEM_STAT_AVERAGEDAMAGE",             "", LocaleGroup.LOCALE_GEMSTAT,   GemStatId.GEM_STAT_AVERAGEDAMAGE ),
            new SettingItem( "GEM_STAT_INTELLIGENCE",              "", LocaleGroup.LOCALE_GEMSTAT,   GemStatId.GEM_STAT_INTELLIGENCE ),
            new SettingItem( "GEM_STAT_MAGICFIND",                 "", LocaleGroup.LOCALE_GEMSTAT,   GemStatId.GEM_STAT_MAGICFIND ),
            new SettingItem( "GEM_STAT_PHYSICALDAMAGETOATTACKER",  "", LocaleGroup.LOCALE_GEMSTAT,   GemStatId.GEM_STAT_PHYSICALDAMAGETOATTACKER ),
            new SettingItem( "HOVER_STAT_PERCENTDAMAGE",           "", LocaleGroup.LOCALE_HOVERSTAT, HoverStatId.HOVER_STAT_PERCENTDAMAGE ),
        };

        //------------------------------------------------------------------------
        public static readonly uint[] ITEM_GEM_LIFEONHIT = new uint[]
        {
            2, 3, 6, 10, 15, 25, 35, 65, 105, 190, 300, 400, 500, 600, 700
        };

        public static readonly uint[] ITEM_GEM_CRITICALHITDAMAGE = new uint[]
        {
            10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100, 110
        };

        public static readonly uint[] ITEM_GEM_BONUSMINIMUMWEAPONDAMAGE = new uint[]
        {
            3, 6, 9, 12, 15, 18, 21, 25, 30, 40, 60, 80, 100, 130, 160
        };

        public static readonly uint[] ITEM_GEM_PHYSICALDAMAGETOATTACKER = new uint[]
        {
            2, 3, 6, 10, 15, 30, 50, 100, 200, 350, 600, 900, 1250, 1800, 2500
        };

        //------------------------------------------------------------------------
        public static readonly string LOCALE_REGISTRY_PATH = "Software\\Blizzard Entertainment\\D3";
        public static readonly string LOCALE_REGISTRY_KEY = "Locale";
        public static readonly string LOCALE_DEFAULT1 = "enUS";
        public static readonly string LOCALE_DEFAULT2 = "enGB";

        //------------------------------------------------------------------------
        public static readonly string GAME_WINDOW_CLASS = "D3 Main Window Class";
        public static readonly uint GAME_GLOBAL_DELAY_MAX = 60000;  // ms
        public static readonly uint GAME_SLEEP_SLICE = 500;    // ms
        public static readonly RangedUnsignedLong GAME_LEVEL_MAX = new RangedUnsignedLong(99, 0);
        public static readonly uint GAME_LOGOUT_DELAY = 1000;
        public static readonly uint GAME_LOGIN_LOOP_DELAY = 500;

        //------------------------------------------------------------------------
        public static readonly uint TRAINER_UIOBJECT_LIMIT = 200;
        public static readonly uint TRAINER_UISEARCH_DEPTH = 15;

        //------------------------------------------------------------------------
        public static readonly int ITEM_STAT_LIMIT = 14;
        public static readonly RangedUnsignedLong ITEM_STAT_VALUE_MAX = new RangedUnsignedLong(99999, 0);
        public static readonly int ITEM_STAT_VALUE_LIMIT = 4;
        public static readonly int ITEM_SOCKET_LIMIT = 3;
        public static readonly int ITEM_GEM_RANK_LIMIT = 15;

        public static readonly IdEnum[] ITEM_GEM_STAT_IDS = new IdEnum[]
        {
            // amethyst
            new IdEnum("",   GemStatId.GEM_STAT_VITALITY ),
            new IdEnum("",   GemStatId.GEM_STAT_LIFEP ),
            new IdEnum("",   GemStatId.GEM_STAT_LIFEONHIT ),

            // emerald
            new IdEnum("",   GemStatId.GEM_STAT_DEXTERITY ),
            new IdEnum("",   GemStatId.GEM_STAT_GOLDFIND ),
            new IdEnum("",   GemStatId.GEM_STAT_CRITICALHITDAMAGE ),

            // ruby
            new IdEnum("",   GemStatId.GEM_STAT_STRENGTH ),
            new IdEnum("",   GemStatId.GEM_STAT_BONUSEXPERIENCE ),
            new IdEnum("",   GemStatId.GEM_STAT_AVERAGEDAMAGE ),

            // topaz
            new IdEnum("",   GemStatId.GEM_STAT_INTELLIGENCE ),
            new IdEnum("",   GemStatId.GEM_STAT_MAGICFIND ),
            new IdEnum("",   GemStatId.GEM_STAT_PHYSICALDAMAGETOATTACKER ),
        };


        public static readonly IdEnum[] ITEM_GEM_TYPE_IDS = new IdEnum[]
        {
            new IdEnum("Empty",      GemTypeId.GEM_TYPE_EMPTY ),
            new IdEnum("Amethyst",   GemTypeId.GEM_TYPE_AMETHYST ),
            new IdEnum("Emerald",    GemTypeId.GEM_TYPE_EMERALD ),
            new IdEnum("Ruby",       GemTypeId.GEM_TYPE_RUBY ),
            new IdEnum("Topaz",      GemTypeId.GEM_TYPE_TOPAZ ),
        };

        public static readonly IdEnum[] ITEM_HOVER_STAT_IDS = new IdEnum[]
        {
            new IdEnum("",   HoverStatId.HOVER_STAT_PERCENTDAMAGE ),
        };

        public static readonly IdEnum[] ITEM_RARITY_COLORS = new IdEnum[]
        {
            new IdEnum("ff888888",   RarityId.RARITY_INFERIOR ),
            new IdEnum("ffffffff",   RarityId.RARITY_NORMAL ),
            new IdEnum("ffffffff",   RarityId.RARITY_SUPERIOR ),
            new IdEnum("ff6969ff",   RarityId.RARITY_MAGIC ),
            new IdEnum("ffffff00",   RarityId.RARITY_RARE ),
            new IdEnum("ffbf642f",   RarityId.RARITY_LEGENDARY ),
            new IdEnum("ff00ff00",   RarityId.RARITY_SET ),
        };

        public static readonly IdEnum[] ITEM_RARITY_STRINGS = new IdEnum[]
        {
            new IdEnum("unknown",    RarityId.RARITY_UNKNOWN ),
            new IdEnum("inferior",   RarityId.RARITY_INFERIOR ),
            new IdEnum("normal",     RarityId.RARITY_NORMAL ),
            new IdEnum("superior",   RarityId.RARITY_SUPERIOR ),
            new IdEnum("magic",      RarityId.RARITY_MAGIC ),
            new IdEnum("rare",       RarityId.RARITY_RARE ),
            new IdEnum("legendary",  RarityId.RARITY_LEGENDARY ),
            new IdEnum("set",        RarityId.RARITY_SET ),
        };

        //------------------------------------------------------------------------
        public static readonly uint AH_PSTAT_LIMIT = 6;
        public static readonly uint AH_BUYOUT_RANDOM_LIMIT = 4000;
        public static readonly uint AH_ITEMHOVER_WAIT_ITERATIONS = 200;
        public static readonly uint AH_NETWORK_WAIT_ITERATIONS = 6000;
        public static readonly uint AH_BUTTON_WAIT_ITERATIONS = 40;
        public static readonly uint AH_RESULTS_ROW_LIMIT = 11;
        public static readonly uint AH_SELL_VISIBLE_LIMIT = 5;
        public static readonly uint AH_SELL_ROW_LIMIT = 10;
        public static readonly uint AH_UNIQUE_ROW_LIMIT = 5;
        public static readonly uint AH_STASH_COLUMNS = 7;
        public static readonly uint AH_STASH_ROWS = 10;
        public static readonly uint AH_STASH_BAGS = 3;


        public static readonly IdEnum[] AH_COMBO_CHAR_IDS = new IdEnum[]
    {
        new IdEnum("Barbarian",      FilterCharId.FILTER_CHAR_BARBARIAN ),
        new IdEnum("Demon Hunter",   FilterCharId.FILTER_CHAR_DEMONHUNTER ),
        new IdEnum("Monk",           FilterCharId.FILTER_CHAR_MONK ),
        new IdEnum("Witch Doctor",   FilterCharId.FILTER_CHAR_WITCHDOCTOR ),
        new IdEnum("Wizard",         FilterCharId.FILTER_CHAR_WIZARD ),
    };

        public static readonly IdEnum[] AH_COMBO_PRIMARY_IDS = new IdEnum[]
    {
        new IdEnum("1-Hand",             FilterPrimaryId.FILTER_PRI_1H ),
        new IdEnum("2-Hand",             FilterPrimaryId.FILTER_PRI_2H ),
        new IdEnum("Off-Hand",           FilterPrimaryId.FILTER_PRI_OH ),
        new IdEnum("Armor",              FilterPrimaryId.FILTER_PRI_ARMOR ),
        new IdEnum("Follower Special",   FilterPrimaryId.FILTER_PRI_FOLLOWER ),
    };

        public static readonly IdEnumEnum[] AH_COMBO_SECONDARY_DEPS = new IdEnumEnum[]
        {
        // 1-Hand
        new IdEnumEnum("All 1-Hand Item Types",      FilterPrimaryId.FILTER_PRI_1H, _C_ALL ),
        new IdEnumEnum("Axe",                         FilterPrimaryId.FILTER_PRI_1H, _C_ALL ),
        new IdEnumEnum("Ceremonial Knife",           FilterPrimaryId.FILTER_PRI_1H, _C_WD ),
        new IdEnumEnum("Hand Crossbow",          FilterPrimaryId.FILTER_PRI_1H, _C_DH ),
        new IdEnumEnum("Dagger",                 FilterPrimaryId.FILTER_PRI_1H, _C_ALL ),
        new IdEnumEnum("Fist Weapon",            FilterPrimaryId.FILTER_PRI_1H, _C_MONK ),
        new IdEnumEnum("Mace",                   FilterPrimaryId.FILTER_PRI_1H, _C_ALL ),
        new IdEnumEnum("Mighty Weapon",          FilterPrimaryId.FILTER_PRI_1H, _C_BARB ),
        new IdEnumEnum("Spear",                  FilterPrimaryId.FILTER_PRI_1H, _C_ALL ),
        new IdEnumEnum("Sword",                  FilterPrimaryId.FILTER_PRI_1H, _C_ALL ),
        new IdEnumEnum("Wand",                   FilterPrimaryId.FILTER_PRI_1H, _C_WIZ ),

        // 2-Hand
        new IdEnumEnum("All 2-Hand Item Types",  FilterPrimaryId.FILTER_PRI_2H, _C_ALL ),
        new IdEnumEnum("Two-Handed Axe",         FilterPrimaryId.FILTER_PRI_2H, _C_BARB|_C_MONK|_C_WD|_C_WIZ ),
        new IdEnumEnum("Bow",                    FilterPrimaryId.FILTER_PRI_2H, _C_DH|_C_WD|_C_WIZ ),
        new IdEnumEnum("Daibo",                  FilterPrimaryId.FILTER_PRI_2H, _C_MONK ),
        new IdEnumEnum("Crossbow",               FilterPrimaryId.FILTER_PRI_2H, _C_DH|_C_WD|_C_WIZ ),
        new IdEnumEnum("Two-Handed Mace",        FilterPrimaryId.FILTER_PRI_2H, _C_BARB|_C_MONK|_C_WD|_C_WIZ ),
        new IdEnumEnum("Two-Handed Mighty Weapon",   FilterPrimaryId.FILTER_PRI_2H, _C_BARB ),
        new IdEnumEnum("Polearm",                FilterPrimaryId.FILTER_PRI_2H, _C_BARB|_C_MONK|_C_WD ),
        new IdEnumEnum("Staff",                  FilterPrimaryId.FILTER_PRI_2H, _C_MONK|_C_WD|_C_WIZ ),
        new IdEnumEnum("Two-Handed Sword",       FilterPrimaryId.FILTER_PRI_2H, _C_BARB|_C_MONK|_C_WD|_C_WIZ ),

        // Off-Hand
        new IdEnumEnum("All Off-Hand Item Types",FilterPrimaryId.FILTER_PRI_OH, _C_ALL ),
        new IdEnumEnum("Mojo",                   FilterPrimaryId.FILTER_PRI_OH, _C_WD ),
        new IdEnumEnum("Source",                 FilterPrimaryId.FILTER_PRI_OH, _C_WIZ ),
        new IdEnumEnum("Quiver",                 FilterPrimaryId.FILTER_PRI_OH, _C_DH ),
        new IdEnumEnum("Shield",                 FilterPrimaryId.FILTER_PRI_OH, _C_ALL ),

        // Armor
        new IdEnumEnum("All Armor Item Types",   FilterPrimaryId.FILTER_PRI_ARMOR, _C_ALL ),
        new IdEnumEnum("Amulet",                 FilterPrimaryId.FILTER_PRI_ARMOR, _C_ALL ),
        new IdEnumEnum("Belt",                   FilterPrimaryId.FILTER_PRI_ARMOR, _C_ALL ),
        new IdEnumEnum("Boots",                  FilterPrimaryId.FILTER_PRI_ARMOR, _C_ALL ),
        new IdEnumEnum("Bracers",                FilterPrimaryId.FILTER_PRI_ARMOR, _C_ALL ),
        new IdEnumEnum("Chest Armor",            FilterPrimaryId.FILTER_PRI_ARMOR, _C_ALL ),
        new IdEnumEnum("Cloak",                  FilterPrimaryId.FILTER_PRI_ARMOR, _C_DH ),
        new IdEnumEnum("Gloves",                 FilterPrimaryId.FILTER_PRI_ARMOR, _C_ALL ),
        new IdEnumEnum("Helm",                   FilterPrimaryId.FILTER_PRI_ARMOR, _C_ALL ),
        new IdEnumEnum("Pants",                  FilterPrimaryId.FILTER_PRI_ARMOR, _C_ALL ),
        new IdEnumEnum("Mighty Belt",            FilterPrimaryId.FILTER_PRI_ARMOR, _C_BARB ),
        new IdEnumEnum("Ring",                   FilterPrimaryId.FILTER_PRI_ARMOR, _C_ALL ),
        new IdEnumEnum("Shoulders",              FilterPrimaryId.FILTER_PRI_ARMOR, _C_ALL ),
        new IdEnumEnum("Spirit Stone",           FilterPrimaryId.FILTER_PRI_ARMOR, _C_MONK ),
        new IdEnumEnum("Voodoo Mask",            FilterPrimaryId.FILTER_PRI_ARMOR, _C_WD ),
        new IdEnumEnum("Wizard Hat",             FilterPrimaryId.FILTER_PRI_ARMOR, _C_WIZ ),

        // Follower Special
        new IdEnumEnum("All Follower Special Item Types", FilterPrimaryId.FILTER_PRI_FOLLOWER, _C_ALL ),
        new IdEnumEnum("Enchantress Focus",      FilterPrimaryId.FILTER_PRI_FOLLOWER, _C_ALL ),
        new IdEnumEnum("Scoundrel Token",        FilterPrimaryId.FILTER_PRI_FOLLOWER, _C_ALL ),
        new IdEnumEnum("Templar Relic",          FilterPrimaryId.FILTER_PRI_FOLLOWER, _C_ALL ),
        };
        public static readonly string AH_POPUP_SUCCESS_CREATEAUCTION = "auction created";
        public static readonly string AH_POPUP_SUCCESS_BUYOUT = "buyout accepted";
        public static readonly string AH_POPUP_SUCCESS_BID = "bid accepted";

        //------------------------------------------------------------------------
        public static readonly uint UI_COORD_SPREAD = 2;
        public static readonly Coordinate[] UI_COORDS = new Coordinate[]
        {
        // UI_BUTTON_SEARCH
            new Coordinate( F2R_XC(627),  F2R_Y(1245) ),
            // UI_BUTTON_BID
            new Coordinate( F2R_XC(1646), F2R_Y(1300) ),
            // UI_BUTTON_BIDCONFIRM
            new Coordinate( F2R_XC(1110), F2R_Y(1159) ),
            // UI_BUTTON_BUYOUT
            new Coordinate( F2R_XC(2000), F2R_Y(1300) ),
            // UI_BUTTON_BUYOUTCONFIRM
            new Coordinate( F2R_XC(1110), F2R_Y(1159) ),
            // UI_BUTTON_CREATEAUCTION
            new Coordinate( F2R_XC(1278), F2R_Y(1299) ),
            // UI_BUTTON_SENDTOSTASH
            new Coordinate( F2R_XC(629),  F2R_Y(664) ),
            // UI_BUTTON_LOGIN
            new Coordinate( F2R_XC(1280), F2R_Y(1265) ),
            // UI_BUTTON_AUCTIONHOUSE
            new Coordinate( F2R_X (345),  F2R_Y(949), CoordinateTypeEnum.LEFT ),
            // UI_BUTTON_OPTIONS
            new Coordinate( F2R_XR(2345), F2R_Y(1500), CoordinateTypeEnum.RIGHT ),
            // UI_BUTTON_OPTIONSLOGOUT
            new Coordinate( F2R_XC(1280), F2R_Y(944) ),
            // UI_BUTTON_PROFILECLOSE
            new Coordinate( F2R_XC(2196), F2R_Y(165) ),

            // UI_LBUTTON_PAGENEXT
            new Coordinate( F2R_XC(1668), F2R_Y(1212) ),
            // UI_LBUTTON_SORTDPSARMOR
            new Coordinate( F2R_XC(1662), F2R_Y(408) ),
            // UI_LBUTTON_SORTBUYOUT
            new Coordinate( F2R_XC(1962), F2R_Y(408) ),
            // UI_LBUTTON_SORTTIMELEFT
            new Coordinate( F2R_XC(2109), F2R_Y(408) ),
            // UI_LBUTTON_CLEARPSTAT0-5
            new Coordinate( F2R_XC(799),  F2R_Y(880) ),
            new Coordinate( F2R_XC(799),  F2R_Y(935) ),
            new Coordinate( F2R_XC(799),  F2R_Y(991) ),
            new Coordinate( F2R_XC(799),  F2R_Y(1048) ),
            new Coordinate( F2R_XC(799),  F2R_Y(1104) ),
            new Coordinate( F2R_XC(799),  F2R_Y(1160) ),
            // UI_LBUTTON_SELLSCROLLUP
            new Coordinate( F2R_XC(2153), F2R_Y(457) ),
            // UI_LBUTTON_SELLSCROLLDOWN
            new Coordinate( F2R_XC(2153), F2R_Y(748) ),
            // UI_LBUTTON_SELLCANCEL0
            new Coordinate( F2R_XC(1907), F2R_Y(468) ),
            // UI_LBUTTON_SELLCANCELSIZE
            new Coordinate( F2R_X(240.0), F2R_Y(66.5) ),

            // UI_TAB_SEARCH
            new Coordinate( F2R_XC(791),  F2R_Y(240) ),
            // UI_TAB_SEARCH_EQUIPMENT
            new Coordinate( F2R_XC(699),  F2R_Y(353) ),
            // UI_TAB_SELL
            new Coordinate( F2R_XC(1122), F2R_Y(240) ),
            // UI_TAB_AUCTIONS
            new Coordinate( F2R_XC(1430), F2R_Y(240) ),
            // UI_TAB_COMPLETED
            new Coordinate( F2R_XC(1750), F2R_Y(240) ),
            // UI_TAB_STASHBAG1-3
            new Coordinate( F2R_XC(1066), F2R_Y(482) ),
            new Coordinate( F2R_XC(1066), F2R_Y(650) ),
            new Coordinate( F2R_XC(1066), F2R_Y(808) ),

            // UI_CONTAINER_STASHBOXSIZE
            new Coordinate( F2R_X(79.57), F2R_Y(82.3) ),
            // UI_CONTAINER_STASHBOX00
            new Coordinate( F2R_XC(472),  F2R_Y(445) ),
            // UI_CONTAINER_LISTICON0
            new Coordinate( F2R_XC(895),  F2R_Y(443) ),
            // UI_CONTAINER_LISTICONSIZE
            new Coordinate( F2R_X(66.91), F2R_Y(66.91) ),
            // UI_CONTAINER_SELLICON0
            new Coordinate( F2R_XC(420),  F2R_Y(472) ),
            // UI_CONTAINER_SELLICONSIZE
            new Coordinate( F2R_X(66.91), F2R_Y(66.91) ),
            // UI_CONTAINER_COMBOROWSIZE
            new Coordinate( F2R_X(100),   F2R_Y(44.8) ),
            // UI_CONTAINER_UNIQUEROWSIZE
            new Coordinate( F2R_X(100),   F2R_Y(51.4) ),

            // UI_INPUT_FILTERLEVELMIN
            new Coordinate( F2R_XC(461),  F2R_Y(667) ),
            // UI_INPUT_FILTERLEVELMAX
            new Coordinate( F2R_XC(570),  F2R_Y(667) ),
            // UI_INPUT_FILTERUNIQUE
            new Coordinate( F2R_XC(808),  F2R_Y(736) ),
            // UI_INPUT_FILTERBUYOUT
            new Coordinate( F2R_XC(808),  F2R_Y(785) ),
            // UI_INPUT_FILTERPSTAT0-5
            new Coordinate( F2R_XC(739),  F2R_Y(880) ),
            new Coordinate( F2R_XC(739),  F2R_Y(935) ),
            new Coordinate( F2R_XC(739),  F2R_Y(991) ),
            new Coordinate( F2R_XC(739),  F2R_Y(1048) ),
            new Coordinate( F2R_XC(739),  F2R_Y(1104) ),
            new Coordinate( F2R_XC(739),  F2R_Y(1160) ),
            // UI_INPUT_MAXBID
            new Coordinate( F2R_XC(1466), F2R_Y(629) ),
            // UI_INPUT_SELLSTARTING
            new Coordinate( F2R_XC(1278), F2R_Y(706) ),
            // UI_INPUT_SELLBUYOUT
            new Coordinate( F2R_XC(1278), F2R_Y(826) ),
            // UI_INPUT_LOGINACCOUNT
            new Coordinate( F2R_XC(1280), F2R_Y(880) ),
            // UI_INPUT_LOGINPASSWORD
            new Coordinate( F2R_XC(1280), F2R_Y(1047) ),

            // UI_COMBO_RARITY
            new Coordinate( F2R_XC(691),  F2R_Y(666) ),
            // UI_COMBO_CHARACTER
            new Coordinate( F2R_XC(484),  F2R_Y(456) ),
            // UI_COMBO_PRIMARY
            new Coordinate( F2R_XC(484),  F2R_Y(512) ),
            // UI_COMBO_SECONDARY
            new Coordinate( F2R_XC(484),  F2R_Y(568) ),
            // UI_COMBO_PSTAT0-5
            new Coordinate( F2R_XC(484),  F2R_Y(881) ),
            new Coordinate( F2R_XC(484),  F2R_Y(937) ),
            new Coordinate( F2R_XC(484),  F2R_Y(993) ),
            new Coordinate( F2R_XC(484),  F2R_Y(1049) ),
            new Coordinate( F2R_XC(484),  F2R_Y(1105) ),
            new Coordinate( F2R_XC(484),  F2R_Y(1161) ),

            // UI_POPUP_AH
            new Coordinate( F2R_XC(1280), F2R_Y(687) ),
            // UI_POPUP_ERROR
            new Coordinate( F2R_XC(1280), F2R_Y(936) ),
            // UI_POPUP_UNIQUE
            new Coordinate( F2R_XC(500),  F2R_Y(798) ),

            // UI_OTHER_GROUND
            new Coordinate( F2R_XC(1280), F2R_Y(4) ),
        };

        public static readonly uint UI_COMBO_ROW_LIMIT = TRAINER_UIOBJECT_LIMIT;
        public static readonly uint UI_COMBO_REZMAP_MIN = 600;
        public static readonly uint UI_COMBO_REZMAP_MAX = 1200;


        // address offsets
        //------------------------------------------------------------------------
        /*
            TOTAL GOLD (used by haGetGold)

            1 while in AH mem search your total gold value (exact/4bytes). select lowest address one found.
            2 pointer scan address found (double click address/right click that entry below/pointer scan this adddress).
              use: static only. levels=2. offset=1024.
              cheat engine asks for a file for tracking this. just pick a random unsued one. you can delete them later.
            3 with pointer scan still open restart d3 and go back to AH. reopen the D3 process in cheat engine. refresh
              pointer scan results by clicking yes when a popup prompts you to keep address list.
              if in pointer scan results you still see your gold value use that address. if few duplicates found use first one.
            4 note this gold value is inside a struct (Trainer.hpp:AhGlobal1.gold). note the offset of that value. if you see
              this as the last offset in pointer scan its likely the correct one.
            5 if no correct value found goto 1 and use an address not previously used. you may have to do this a few times!
        */
        public static readonly uint[] _CHAIN_GOLD = new uint[] { 0x00EA7A58 }; // _MODULE_D3

        /*
            LOGIN NAME (used by haGetLogin)

            1 while logged in mem search your account name (exact/string). select lowest address one found.
            2 pointer scan address found. static only. levels=3. offset=1024. these should look similar to old addresses:
              the 2nd offset likely remaining the same. the 3rd possibly changing a little.
            3 with pointer scan still open restart d3 login and verify
            4 if no correct value found goto 1 and select another address.
        */
        public static readonly uint[] _CHAIN_ACCOUNT = { 0x00FF9874, 0x0010, 0x0274 }; // _MODULE_D3

        /*
            FRAME COUNTER (used by everything)

            1 set a low/minimum frame rate in video options.
            2 mem search (value between/4byte) between estimated frames passed. its best to do soon after d3 starts at
              login screen with an initial range of about 10 to 50 after waiting several seconds.
            3 mem research next scan using increased value. do this several times. will need to goto 5 to fully narrow down.
            4 ensure valid value in all parts of the game: menus, game, and AH
            5 restart d3 goto 2. do this until narrow down to under 30 addresses.
            6 for each address keep only static addresses (will appear as green text in memory view). you will likely narrow
              it down to 2 values. select the one that appears to be lagging behind a little.

            IMPORTANT: use the module offset address, not absolute address! easy way to add the address to the address list then
              right click it/change record/address. then copy "Diablo III.exe"+<THISVALUE>
        */
        public static readonly uint _BASEADDRESS_FRAMECOUNT = 0x01130D44; // _MODULE_D3

        /*
            AUCTION SEARCH RESULTS (used by haList*)

            1 d3 search such that first page of results is less than 11 items. i recommend choosing an armor filter like
              Dexterity and setting it really high.
            2 mem search (exact/4bytes) count of items
            3 goto 1 and change search results so the value changes a little. do this until narrowed down to a small list of addresses.
            4 for each address found open memory viewer (right click/browse memory region). look at address value +8 over in another memory
              window (file/new window then right click/goto address) and verify it matches the format of Trainer.hpp:AhListAuction:
              you should see D3_GOLD and D3_ITEM text visible in text portion of memory window.
            5 if address with correct format found pointer scan this address. static only. levels=2. offset=1024.
              should look similar to old addresses + offset of Trainer.hpp:AhListRoot.auction_count. exclude this offset in update.
            6 with pointer scan still open restart d3 goto 1. however you will likely have the correct address first scan.

            Updating AhListAuction

            1 run a log script like LogAuctions.lua to see if things are correct. if not proceed to next step.
            2 the address location found points to AhListRoot. AhListRoot.auction_items points to an array of AhListRoot.auction_count
              AhListAuction objects. the format of this MAY change if blizzard makes heavy AH updates.
            3 open the memory editor and go to to AhListRoot.auction_items. ensure the values found line up according to AhListAuction.
              including the total length of this object.
            4 this is important since the majority of haItem() gets its info from this.
        */
        public static readonly uint[] _CHAIN_SEARCHRESULTS = { 0x00EAA500, 0x00000000 }; // _MODULE_D3

        /*
            SELL LIST (used by haSellCancel and haSellIterate)

            1 visit auctions tab and ensure you have some items up for sale
            2 mem search 4byte count of items selling. remove/add items and do again to narrow down.
            3 at this point follow the same instructions as above section using AhListRoot.sell_* and AhListSell.
            4 the base address is consistently very close to that search results
        */
        public static readonly uint[] _CHAIN_SELLS = { 0x00EAA550, 0x00000000 }; // _MODULE_D3

        /*
            IS LOGGED IN (used by haSetLogin())

            1 login and mem search 4byte = 1
            2 logout and mem search 4byte = 0. it should remain 0 during login sequence until the very end.
            3 ensure value is 1 in menus and AH. it will be 0 in game.
            4 restart d3 goto 1. this may take a few tries.
            5 keep only static address. you may find several addresses that all behave the same. pick the lowest one.

            IMPORTANT: use the module offset address, not absolute address!
        */
        public static readonly uint _BASEADDRESS_LOGGEDIN = 0x00EA7B58; // _MODULE_D3

        /*
            SELL LIST EXTERNAL COUNT (this supplements an internal requirement to SELL LIST)

            1 visit auctions main. but not auctions tab
            2 mem search count of items selling (based on auctions tab "(number)" text)
            3 add/cancel items to change this count
            4 exit auctions and goto 1 until narrowed down.
            5 pointer scan address found. static only. levels=2. offset=1024.
              should look similar to old addresses + offset of AhGlobal2.sellcount
            6 confirm with d3 restart
        */
        public static readonly uint[] _CHAIN_SELLCOUNT = { 0x00EA9080, 0x00000000 }; // _MODULE_D3


    }

    public class RangedUnsignedLong
    {
        private ulong mMax = 0;
        private ulong mMin = 0;
        private ulong mValue = 0;

        public RangedUnsignedLong(ulong max, ulong min)
        {
            mMax = max;
            mMin = min;

            mValue = min;
        }

        public void SetValue(ulong value)
        {
            if (value < mMin || value > mMax)
            {
                throw new IndexOutOfRangeException(string.Format("Max={0}, Min={1}", mMax, mMin));
            }

            mValue = value;
        }

        public ulong GetValue()
        {
            return mValue;
        }

        
    }
}

